#pragma once

#include <type_traits>
#include <utility>
#include <thread>
#include <chrono>

template <bool B, class T = void>
struct disable_if_c {
    typedef T type;
};

template <class T>
struct disable_if_c<true, T> {};

template <class Cond, class T = void> 
struct disable_if : public disable_if_c<Cond::value, T> {};

struct join_if_joinable
{
    template <class Thread>
    void operator()(Thread &t)
    {
        if (t.joinable())
        {
            t.join();
        }
    }
};

template <class CallableThread = join_if_joinable>
class scoped_thread
{
    std::thread t_;
    struct dummy;
public:
    typedef typename std::thread::id id;
    typedef typename std::thread::native_handle_type native_handle_type;

    // BOOST_THREAD_MOVABLE_ONLY( scoped_thread) /// Movable only

    scoped_thread() noexcept:
    t_()
    {
    }

    template <class F, class ...Args, typename = typename disable_if<std::is_same<typename std::decay<F>::type, std::thread>, void* >::type>
    explicit scoped_thread(F&& f, Args&&... args) :
      t_(std::forward<F>(f), std::forward<Args>(args)...) {}

    explicit scoped_thread(std::thread&& t) noexcept :
    t_(std::move(t))
    {
    }

//    explicit operator Thread()
//    {
//      return boost::move(t_);
//    }

    /**
     * Move constructor.
     */
    scoped_thread(scoped_thread&& x) noexcept :
    t_(std::move(x.t_))
    {}

    ~scoped_thread()
    {
      CallableThread on_destructor;

      on_destructor(t_);
    }

    scoped_thread& operator=(scoped_thread&& x)
    {
      CallableThread on_destructor;

      on_destructor(t_);
      t_ = std::move(x.t_);
      return *this;
    }

    void swap(scoped_thread& x) noexcept
    {
      t_.swap(x.t_);
    }

    inline id get_id() const noexcept
    {
      return t_.get_id();
    }

    void detach()
    {
      t_.detach();
    }

    void join()
    {
      t_.join();
    }

    native_handle_type native_handle() noexcept
    {
      return t_.native_handle();
    }

    bool joinable() const noexcept
    {
      return t_.joinable();
    }

    static unsigned hardware_concurrency() noexcept
    {
      return std::thread::hardware_concurrency();
    }

  };

  template <class Destroyer>
  void swap(scoped_thread<Destroyer>& lhs, scoped_thread<Destroyer>& rhs) noexcept {
  return lhs.swap(rhs);
};

typedef scoped_thread<> joining_thread;


#if 0
#include "scoped_thread.h"
#include <iostream>
#include <thread>

int main()
{
        auto f = []() {
                        std::cout << "haha " << std::this_thread::get_id() << std::endl;
        };

#if 1
        scoped_thread<> t([](){
                        std::cout << "haha " << std::this_thread::get_id() << std::endl;
                        });
#else
        scoped_thread<> t(f);
#endif
        std::cout << "dada " << t.get_id() << std::endl;
        std::cout << "lala " << std::this_thread::get_id() << std::endl;
}
#endif
